/*
 @Name: tree插件
 @Author: ray
 @License: MIT
*/
; rayui.define(["jqlibs", "layer"], function (exports, undef) {
    "use strict";

    var $ = rayui.$, $body,
        layer = rayui.layer,
        $doc = $(document),
        plugName = "tree",
        globalIndex = 1000,
        dataCache = {
            mclass: {},
            datas: {}
        },
        globalOptions = {
            ajaxData_Code: "ret",
            ajaxData_Msg: "msg",
            ajaxData_Data: "data",
            width: -1, //自适应
            height: -1,//自适应
            loading: true,
            localSearch: false, //是否允许本地搜索
            fieldName: "name", //显示列
            fieldChildren: "children", //子节点
            icon: true, //是否使用图标
            iconSpread: true,//展开折叠的图标
            spread: false, //默认折叠
            bgColors: false, //是否显示背景色
            animSpeed: 200,//动画执行时间
            dragGap: 5//拖拽间隙，移动大于5px，认为是拖拽
        },
        DealClass = function (options) {
            var that = this;
            that.index = globalIndex;

            if (options.data && $.isArray(options.data))
                dataCache.datas[that.index] = options.data;
            else
                dataCache.datas[that.index] = [];

            //处理数据
            if (typeof options.inputArea === "string")
                options.inputArea = [options.inputArea];

            that.options = $.extend(true, {}, globalOptions, options);

            DealClass.utils.init.call(that);
        };

    DealClass.utils = {
        onRecvData: function () {
            var that = this,
                options = that.options;

            DealClass.utils.renderData.call(that);
            DealClass.utils.initStyle.call(that);

            if (options.initComplete == undef) {
                options.initComplete = true;
                DealClass.utils.addEvents.call(that);
            }
            //所有处理完毕
            typeof options.onComplete === "function" && options.onComplete();
        },
        init: function () {
            var that = this,
                options = that.options;

            options.$main = $('<div class="rayui-tree"/>').appendTo($(options.elem));

            //是否允许本地过滤
            if (options.localSearch === true) {
                var $obj = $('<div class="rayui-tree-input"><input type="text" class="rayui-input" placeholder="关键词"/></div>').appendTo(options.$main);
                options.$input = $obj.find("input");
            } else if ($.isJqueryDom($(options.localSearch))) {
                options.$input = $(options.localSearch);
            }
            options.$databox = $('<div class="tree-data-panel"/>').appendTo(options.$main);
            options.skin && options.$databox.attr("skin", options.skin);
            //加载数据
            DealClass.utils.reload.call(that);
            typeof options.onShow === "function" && options.onShow.call(that, $container);
        },
        initStyle: function () {
            var that = this,
                options = that.options;

            //调整input宽高
            if (options.localSearch && options.inputArea) {
                var whOption = {};
                whOption.width = options.inputArea[0];
                options.inputArea.length > 1 && (whOption.height = options.inputArea[1]);
                options.$input.css(whOption);
            }

            //调整数据宽度
            if (typeof options.width === "string") {
                options.$main.css("width", options.width);
            } else if (options.width >= 0) {
                options.$main.width(options.width);
            } else {
                options.$main.css("width", "");
            }

            //调整数据高度
            if (typeof options.height === "string") {
                options.$databox.css("height", options.height);
            } else if (options.height >= 0) {
                var allowheight = options.height;
                options.$databox.outerHeight(allowheight - (options.$main.find(".rayui-tree-input").outerHeight() || 0));
            } else {
                options.$databox.css("height", "");
            }

        },
        loading: function (type) {
            var that = this,
                options = that.options;
            if (!options.loading) return;

            if (type === 0) {
                options.$databox.removeClass("temp-width-height");
                layer.close(options.layerLoadingIndex);
                return;
            }

            if (type === 1 && options.url) {
                //因为没有数据导致options.$databox没有高度和宽度，需要指定一个
                options.$databox.addClass("temp-width-height");
                options.layerLoadingIndex = layer.loading({
                    container: options.$databox,
                    shade: false,
                    shadow: false,
                    offset: 2
                });
            }
        },
        showError: function (msg) {
            var that = this,
                options = that.options;

            if (msg === 0) {
                options.$databox.removeClass("temp-width-height");
                layer.close(options.layerErrorIndex);
                return;
            }
            //因为没有数据导致options.$databox没有高度和宽度，需要指定一个
            options.$databox.addClass("temp-width-height");
            options.layerErrorIndex = layer.msg({ container: options.$databox, shade: false, shadow: false, offset: 2, content: msg });
        },
        reload: function (option) {
            option !== undef && $.extend(true, this.options, option);
            var that = this,
                options = that.options;

            //没有写url，认为是本地
            if (options.url) {
                //ajax请求数据
                DealClass.utils.ajaxData.call(that);
            } else {
                DealClass.utils.onRecvData.call(that);
            }
        },
        ajaxData: function () {
            var that = this,
                options = that.options;

            DealClass.utils.loading.call(that, 1);
            $.ajax({
                type: options.method || "get",
                url: options.url,
                data: options.where,
                dataType: "json",
                beforeSend: function (xhr) {
                    if (typeof options.onAjaxBeforeSend === "function" &&
                        options.onAjaxBeforeSend.call(this, xhr) === false) {
                        xhr.abort();
                    }
                },
                success: function (result) {
                    //清除数据
                    DealClass.utils.clearData.call(that);
                    if (typeof options.onAjaxSuccess === "function") {
                        var retData = options.onAjaxSuccess.call(this, result);
                        if (retData == null || typeof retData !== "object") return;
                        result = retData;
                    }
                    //result默认数据格式：ret,msg,data
                    var ret = result[options.ajaxData_Code];
                    if (ret !== 0) {
                        DealClass.utils.showError.call(that, (result[options.ajaxData_Msg] || "返回的数据状态异常"));
                    }
                    dataCache.datas[that.index] = result[options.ajaxData_Data] || [];
                    ret === 0 && DealClass.utils.onRecvData.call(that);
                },
                error: function (xhr, textStatus, errorThrown) {
                    DealClass.utils.showError.call(that, "请求数据接口异常");
                    //所有处理完毕
                    typeof options.onAjaxError === "function" && options.onAjaxError.call(this, xhr, textStatus, errorThrown);
                },
                complete: function () {
                    DealClass.utils.loading.call(that, 0);
                }
            });
        },
        clearData: function () {
            var that = this,
                options = that.options;

            options.$main.find("ul").remove();
        },
        getData: function (indexes) {
            var that = this,
                options = that.options,
                datas = dataCache.datas[that.index];

            if (indexes === undef) return datas;
            var count = indexes.length,
                i = 1,
                data = datas[indexes[0]];
            while (i < count)
                data = data[options.fieldChildren][indexes[i++]];
            return data;
        },
        delData: function (indexes) {
            var that = this,
                options = that.options,
                len = indexes.length,
                index = len === 1 ? undef : indexes.slice(0, indexes.length - 1),
                data = DealClass.utils.getData.call(that, index);

            if (len === 1)
                data.splice(indexes[0], 1);
            else
                data[options.fieldChildren].splice(indexes[len - 1], 1);
        },
        delDataBySign: function () {
            var that = this,
                options = that.options,
                data = DealClass.utils.getData.call(that);

            var del = function(data) {
                var len = data.length, index=0;
                while (index < len) {
                    if (data[index].needDelete) {
                        data.splice(index, 1);
                        return true;
                    }
                    if (data[index].hasOwnProperty(options.fieldChildren)) {
                        if(del(data[index][options.fieldChildren])) return true;
                    }
                    index++;
                }
                return false;
            }
            del(data);
        },
        addData: function (indexes, addData) {
            var that = this,
                options = that.options,
                len = indexes.length,
                index = len === 1 ? undef : indexes.slice(0, indexes.length - 1),
                data = DealClass.utils.getData.call(that, index);

            if (len === 1)
                data.splice(indexes[indexes.length - 1], 0, addData);
            else {
                if (!data.hasOwnProperty(options.fieldChildren)) {
                    data[options.fieldChildren] = [];
                }
                data[options.fieldChildren].splice(indexes[indexes.length - 1], 0, addData);
            }
        },
        spreadData: function (indexes, flag) {
            if (indexes.length === 0) return;
            var that = this,
                options = that.options,
                datas = DealClass.utils.getData.call(that),
                count = indexes.length,
                i = 1,
                data = datas[indexes[0]];
            //展开必须逐级都展开，关闭只关闭最后一级
            if (flag) data.spread = true;
            while (i < count) {
                data = data[options.fieldChildren][indexes[i++]];
                if (flag) data.spread = true;
            }
            if (!flag) data.spread = false;
        },
        renderData: function (datasearch) {
            var that = this,
                options = that.options,
                odata = datasearch || DealClass.utils.getData.call(that),
                $main = options.$databox,
                innerUl = '<ul class="tree-innerUl ' + (options.bgColors ? "tree-bgcolor" : "") + '"></ul>';

            //清除数据
            DealClass.utils.clearData.call(that);

            //添加数据
            var addRow = function (data, $parent, iteration, preindex) {
                preindex = preindex || "";
                $(data).each(function (a, b) {
                    var li = [],
                        hasChildren = b.hasOwnProperty(options.fieldChildren) && b[options.fieldChildren].length > 0,
                        spread = (b.spread === undef ? options.spread : b.spread); //默认折叠

                    //添加数据前
                    typeof options.onBeforeAddRow === "function" && options.onBeforeAddRow(a, b);
                    li.push('<li data-index=[' + preindex + a + ']');
                    if (options.fieldId) {
                        li.push(' data-id=' + b[options.fieldId]);
                    }
                    li.push(' class="');
                    li.push(spread ? 'tree-spread' : '');
                    li.push('"');//end class
                    li.push(' iteration=' + iteration);
                    li.push('>');

                    //占位符
                    for (var i = 0; i < iteration; i++) {
                        li.push('<span class="tree-hit tree-expand"></span>');
                    }

                    if (hasChildren)
                        options.iconSpread && li.push('<span class="tree-hit btn-treespread"><i class="ra ra-collapsed"></i></span>');//展开折叠
                    else
                        li.push('<span class="tree-hit treegrid-expand"></span>');//占位符

                    //数据a标签
                    li.push('<a href="javascript:void(0)"');
                    li.push(' class="');
                    b.disabled && li.push('rayui-disabled');//是否禁用
                    li.push('">');//end class

                    //图标
                    if (hasChildren) {
                        //文件夹
                        options.icon && li.push('<span class="tree-hit"><i class="ra ra-folder-close"></i></span>');
                    } else {
                        //文件
                        options.icon && li.push('<span class="tree-hit"><i class="ra ra-file"></i></span>');
                    }

                    var value;
                    if (typeof options.formatter === "function") {
                        //formatter函数
                        value = options.formatter(b[options.fieldName], b);
                    } else {
                        value = b[options.fieldName];
                        if (options.dataType === "html" && value !== undef && value !== '')
                            value = value.replace(/[<>&"]/g, function (c) { return { '<': '&lt;', '>': '&gt;', '&': '&amp;', '"': '&quot;' }[c]; });
                    }

                    li.push('<span class="tree-title">' + value + '</span>');

                    li.push('</a></li>');
                    var strHtml = li.join('');
                    var $li = $("" + strHtml);
                    $li.appendTo($parent);
                    //编辑
                    if (typeof options.edit === "function") {

                        var $editPanel = $('<span class="tree-edit"></span>').appendTo($li.find("a")),
                            back = options.edit(b, $editPanel);
                        typeof back === "string" && $editPanel.append(back);
                    }

                    //添加数据后
                    typeof options.onAddRow === "function" && options.onAddRow(a, $li, b);

                    if (hasChildren) {
                        var $innerUl = $(innerUl).appendTo($li);
                        addRow(b[options.fieldChildren], $innerUl, iteration + 1, preindex + a + ",");
                    }
                });
            }

            addRow(odata, $(innerUl).appendTo($main), 0);
            odata = null;
        },
        search: function (searchTxt) {
            var that = this,
                options = that.options,
                data = $.extend(true, [], DealClass.utils.getData.call(that));

            data = data.search(options.fieldName, searchTxt, options.fieldChildren, function (a) {
                a[options.fieldName] = a[options.fieldName].replace(searchTxt, "<span class='tree-searchText'>" + searchTxt + "</span>");
            });
            DealClass.utils.renderData.call(that, data);
        },
        addEvents: function () {
            var that = this,
                options = that.options,
                $databox = options.$databox,
                isdrag=false;

            //折叠和展开
            $databox.on('click.' + plugName, '.btn-treespread', function (e) {
                var $li = $(this).closest("li"),
                    indexes = $li.data("index"),
                    $ul = $li.find(">ul"),
                    isSpread = $li.hasClass("tree-spread"),
                    data;

                options.onExpanded &&
                    (data = (function () {
                        var datas = DealClass.utils.getData.call(that, indexes);
                        datas = $.extend({}, datas);
                        delete datas[options.fieldChildren];
                        return datas;
                    })());

                //折叠情况反馈到数据中
                DealClass.utils.spreadData.call(that, indexes, !isSpread);

                if (isSpread) {
                    //折叠
                    $ul.slideUp(options.animSpeed, function () {
                        $li.removeClass("tree-spread");
                        options.onExpanded && options.onExpanded($li, data, !isSpread);
                    });
                } else {
                    //展开
                    $ul.slideDown(options.animSpeed, function () {
                        $li.addClass("tree-spread");
                        $ul.css("display","");
                        options.onExpanded && options.onExpanded($li, data, !isSpread);
                    });
                }
            });

            //查询
            if (options.$input) {
                options.$input.on("keyup." + plugName, function () {
                    DealClass.utils.search.call(that, $(this).val());
                });
            }

            //点击事件
            $databox.on('click.' + plugName, 'li > a:not(".rayui-disabled")', function () {
                if (isdrag) return;
                var hasClickCall = typeof options.onSelect === "function";
                if (hasClickCall) {
                    var $li = $(this).closest("li"),
                        indexes = $li.data("index"),
                        data = DealClass.utils.getData.call(that, indexes);

                    //排除data的children节点
                    data = $.extend({}, data);
                    delete data[options.fieldChildren];
                    options.onSelect.call(this, data, $li);
                }
            });

            //拖拽事件
            if (options.drag) {
                var dragOpt = options.drag = {};
                $databox.on('mousedown.' + plugName, 'li > a:not(".rayui-disabled")', function (e) {
                    dragOpt.dragState = 0;
                    dragOpt.Y = e.pageY;
                    dragOpt.from = $(this);
                    dragOpt.show = $(this).clone(true);
                    e.preventDefault();
                }).on('mousemove.' + plugName, 'li > a:not(".rayui-disabled")', function (e) {
                    if (dragOpt.dragState === 1) {
                        if (dragOpt.from.not(this).length === 0) return;
                        dragOpt.to = $(this);
                        //判断在前、在后、还是子集
                        var offset = $(this).offset(),
                            oleft = offset.left,
                            otop = offset.top,
                            obottom = otop + $(this).outerHeight(),
                            pW = dragOpt.$addPosition.outerWidth() + 1,
                            pHalfHeight = 15,//如果移动元素高度变化，此处也需要修改
                            pX = pW, pY;
                        //debugger 
                        if (e.pageY - otop < 5) {
                            pY = otop - pHalfHeight;
                            dragOpt.addPosition = 0;
                        } else if (obottom - e.pageY > 0 && obottom - e.pageY < 5) {
                            pY = obottom - pHalfHeight;
                            dragOpt.addPosition = 2;
                        } else {
                            pY = otop - pHalfHeight + $(this).outerHeight() / 2;
                            dragOpt.addPosition = 1;
                        }
                        if ($(this).siblings(".btn-treespread").length > 0)
                            pX += 18;
                        dragOpt.$addPosition.css({
                            left: oleft - pX,
                            top: pY
                        });
                    }
                }).on('mouseleave.' + plugName, 'li > a:not(".rayui-disabled")', function () {
                    if (dragOpt.dragState === 1) {
                        dragOpt.to = null;
                        dragOpt.addPosition = -1;
                        dragOpt.$addPosition.css("left", "-10000000px");
                    }
                });
                //$doc需要注册多次以允许一个页面有多个tree拖拽
                $doc.on('mousemove.' + plugName, function (e) {
                    if (dragOpt.dragState === 0) {
                        if (Math.abs(e.pageY - dragOpt.Y) > options.dragGap) {
                            dragOpt.dragState = 1;
                            isdrag = true;
                            dragOpt.$move = $('<div class="rayui-tree-drag ' + (options.dragClass || "") + '"/>').append(dragOpt.show).appendTo($body);
                            dragOpt.$addPosition = $('<div class="rayui-tree-addposion ' + (options.dragClass || "") + '"/>').append('<i class="ra ra-linearrow-right"></i>').appendTo($body);
                            //添加样式
                            dragOpt.from.addClass("tree-drag-from");
                            //折叠原来标签btn-treespread
                            dragOpt.from.closest("li").removeClass("tree-spread");
                            $body.css('cursor', "pointer");
                            typeof options.onDragStart === "function" && options.onDragStart.call(dragOpt.from[0]);
                        }
                    } else if (dragOpt.dragState === 1) {
                        dragOpt.$move.css({
                            left: e.pageX,
                            top: e.pageY
                        });
                        typeof options.onDragMoving === "function" && options.onDragMoving.call(dragOpt.from[0]);
                    }
                }).on('mouseup.' + plugName, function () {
                    var dragMove = dragOpt;
                    dragOpt = options.drag = {};
                    if (dragMove.dragState === 1) {
                        dragMove.$addPosition.remove();
                        dragMove.$move.remove();
                        //去掉样式
                        dragMove.from.removeClass("tree-drag-from");
                        $body.css('cursor', "");
                        //取消isdrag
                        setTimeout(function () { isdrag = 0;}, 0);
                        /*
                         * 移动json节点的算法思路，删除元数据，添加到新的节点，实现方法：
                         * 1、复制要删除的节点（下面叫做a）数据，包含children
                         * 2、a数据打上删除标记，本事例用的needDelete，
                         * 3、把复制的数据插入新的节点
                         * 4、遍历所有数据，按照删除标记删除数据a
                         *
                         * 这种实现感觉太傻，如果谁有更好的算法或则想法希望告知(^-^)
                         */
                        if (dragMove.to != null) {
                            var fromIndexes = dragMove.from.closest("li").data("index"),
                                toIndexes = dragMove.to.closest("li").data("index"),
                                addData = DealClass.utils.getData.call(that, fromIndexes),
                                lastIndex = toIndexes.length - 1;

                            //addPosition:0上 1中 2下，其中上不用管
                            if (dragMove.addPosition === 1) {
                                var toData = DealClass.utils.getData.call(that, toIndexes),
                                    childrenCount = toData.hasOwnProperty(options.fieldChildren) ? toData[options.fieldChildren].length : 0;
                                toIndexes.push(childrenCount);
                            } else if (dragMove.addPosition === 2) {
                                toIndexes[lastIndex]++;
                            }

                            //回调
                            if (typeof options.onDragEnd === "function") {
                                var parentData = {}, len = toIndexes.length;
                                if (len === 1 && dragMove.addPosition !== 1) {
                                    parentData[(options.fieldId||"id")] = -1;
                                    parentData[options.fieldName] = "顶级节点";
                                } else {
                                    parentData = DealClass.utils.getData.call(that, toIndexes.slice(0, len - 1));
                                }

                                //如果返回false，则不再进行数据操作
                                if (options.onDragEnd.call(dragMove.to[0], {
                                    elem: dragMove.from[0],
                                    fromData: addData,
                                    toParentData: parentData,
                                    toIndex: toIndexes.slice(toIndexes.length - 1),
                                    position: dragMove.addPosition
                                }) === false) return;
                            }

                            var addData1 = $.extend(true, {}, addData);
                            //给将要删除的数据打标记
                            addData.needDelete = true;
                            //添加
                            DealClass.utils.addData.call(that, toIndexes, addData1);
                            //删除原来数据
                            DealClass.utils.delDataBySign.call(that);
                            //重置数据
                            DealClass.utils.onRecvData.call(that);
                        }
                    }
                });
            }

        },
        on: function (event, func) {
            if (typeof event !== "string" || typeof func !== "function") return;
            var that = this,
                options = that.options;

            switch (event) {
                //工具条
                case "edit":
                    options.$databox.on('click.' + plugName + 'Edit', '.tree-edit *[event]', function () {
                        var evt = $(this).attr("event"),
                            $li = $(this).closest("li"),
                            indexes = $li.data("index"),
                            data = (function () {
                                var datas = DealClass.utils.getData.call(that, indexes);
                                datas = $.extend({}, datas);
                                delete datas[options.fieldChildren];
                                return datas;
                            })(),
                            $datali = {
                                index: indexes,
                                data: data,
                                li: $li,
                                add: function (mm) {
                                    //增加本地数据
                                    var pdata = DealClass.utils.getData.call(that, indexes);
                                    if (!pdata.hasOwnProperty(options.fieldChildren))
                                        pdata[options.fieldChildren] = [];
                                    pdata[options.fieldChildren].push(mm);
                                    //本地重新渲染数据
                                    DealClass.utils.onRecvData.call(that);
                                },
                                del: function () {
                                    //删除本地数据
                                    DealClass.utils.delData.call(that, indexes);
                                    //本地重新渲染数据
                                    DealClass.utils.onRecvData.call(that);
                                },
                                update: function () {
                                    //更新本地数据
                                    var pdata = DealClass.utils.getData.call(that, indexes);
                                    $.extend(pdata, data);
                                    //本地重新渲染数据
                                    DealClass.utils.onRecvData.call(that);
                                }
                            };
                        func.call(this, evt, $datali);
                    });
                    break;
                //以下事件写相应属性事件也是可以的
                //折叠展开行
                case "expanded": options.onExpanded = func; break;
                //点击
                case "select": options.onSelect = func; break;
                //drag开始
                case "dragstart": options.onDragStart = func; break;
                //drag移动中
                case "dragmoving": options.onDragMoving = func; break;
                //drag结束
                case "dragend": options.onDragEnd = func; break;
            }
        }
    }

    DealClass.prototype = {
        on: function (event, callback) {
            DealClass.utils.on.call(this, event, callback);
			return this;
        },
        search: function (str) {
            DealClass.utils.search.call(this, str);
			return this;
        },
        reload: function (options) {
            DealClass.utils.reload.call(this, options);
			return this;
        },
        getData: function (index) {
            return DealClass.utils.getData.call(this, index);
        },
        setWidth: function (w) {
            this.options.width = w;
            DealClass.utils.initStyle.call(this);
			return this;
        },
        setHeight: function (h) {
            this.options.height = h;
            DealClass.utils.initStyle.call(this);
			return this;
        },
        setWidthHeight: function (w, h) {
            this.options.width = w;
            this.options.height = h;
            DealClass.utils.initStyle.call(this);
			return this;
        }
    }

    $(function () {
        $body = $("body");
    });

    var tree = {
        options: globalOptions,
        render: function (options) {
            var obj = options.elem;
            if ($(obj).length === 0) return "DOM对象不存在";

            var cc = new DealClass(options);
            dataCache.mclass[globalIndex++] = cc;
            return cc;
        }
    };

    exports(plugName, tree);
}, rayui.jsAsync());
